﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics.Contracts;

using System.Xml;

namespace SimpleIR
{
	public sealed class IRBasicBlock : IRT1T2Region
	{
		private readonly List<IRByteCode> m_bytecodes;
		private readonly HashSet<IRVarName> m_inScopeAtEntry;
		private readonly HashSet<IRVarName> m_liveAtEntry;
		private readonly Dictionary<IRVarName, IRType> m_localstotypemap;
		private readonly long m_blockID;

		private int m_blockentryumloc;

		[ContractInvariantMethod]
		private void ObjectInvariant()
		{
			Contract.Invariant(this.m_bytecodes != null);
			Contract.Invariant(this.m_inScopeAtEntry != null);
			Contract.Invariant(this.m_liveAtEntry != null);
			Contract.Invariant(this.m_localstotypemap != null);
		}

		public IRBasicBlock(List<IRByteCode> sl, long bid)
		{
			Contract.Requires(sl != null);

			this.m_inScopeAtEntry = new HashSet<IRVarName>();
			this.m_liveAtEntry = new HashSet<IRVarName>();
			this.m_localstotypemap = new Dictionary<IRVarName, IRType>();
			this.m_blockID = bid;
			this.m_bytecodes = sl;
			this.m_blockentryumloc = -1;
		}

		/// <summary>
		/// Set the variables that are in scope at call entry.
		/// </summary>
		/// <param name="inscope">List of variable names that are in scope at the entry of this block.</param>
		public void SetInScopeVars(List<IRVarName> inscope, Dictionary<IRVarName, IRType> localtotypemap)
		{
			Contract.Requires(inscope != null);
			Contract.Requires(localtotypemap != null);

			this.m_inScopeAtEntry.UnionWith(inscope);

			foreach(KeyValuePair<IRVarName, IRType> ltye in localtotypemap)
				this.m_localstotypemap.Add(ltye.Key, ltye.Value);
		}

		/// <summary>
		/// Get all of the address taken variables in this block.
		/// </summary>
		public void GatherAddressTakenLocals(HashSet<IRVarName> addrtakenvars)
		{
			for(int i = 0; i < this.m_bytecodes.Count; ++i)
			{
				if(this.m_bytecodes[i].OperationCode == OpCode.Op.LoadLocalAddr)
					addrtakenvars.Add(((IRLoadToEvalStackBasic)this.m_bytecodes[i]).LocalVar);
			}
		}

		public override String ToString()
		{
			String idstr = "        ";
			StringBuilder sb = new StringBuilder("        L" + this.m_blockID + ":\n");
			sb.Append(idstr + "@Entry=" + this.m_blockentryumloc + "\n");

			for(int i = 0; i < this.m_bytecodes.Count; ++i)
			{
				IRByteCode istmt = this.m_bytecodes[i];

				sb.Append(idstr + istmt.ToString());
				sb.Append("   //" + istmt.GenStmtNumber() + "\n");
			}

			return sb.ToString();
		}

		public override void DGMLDisplayString(XmlWriter xwn, XmlWriter xwl, bool toplevel)
		{
			StringBuilder sb = new StringBuilder("L: " + this.m_blockID + "\n");
			
			for(int i = 0; i < this.m_bytecodes.Count; ++i)
			{
				IRByteCode istmt = this.m_bytecodes[i];

				sb.Append(istmt.ToString());
				sb.Append("   //" + istmt.GenStmtNumber() + "\n");
			}

			xwn.WriteStartElement("Node");
			xwn.WriteAttributeString("Id", this.GenDisplayNameString());
			xwn.WriteAttributeString("Label", sb.ToString());
			xwn.WriteEndElement();

			List<uint> allbt = new List<uint>();
			this.GetAllBranchTargets(allbt);
			foreach(uint trgt in allbt)
			{
				xwl.WriteStartElement("Link");
				xwl.WriteAttributeString("Source", this.BlockID.ToString());
				xwl.WriteAttributeString("Target", trgt.ToString());
				xwl.WriteEndElement();
			}
		}

		public override string GenDisplayNameString()
		{
			return this.BlockID.ToString();
		}

		/// <summary>
		/// Add all the possible branch target from this block to the list.
		/// </summary>
		public void GetAllBranchTargets(List<uint> trgts)
		{
			Contract.Requires(trgts != null);

			if(this.m_bytecodes.Count != 0)
				this.m_bytecodes[this.m_bytecodes.Count - 1].GetBranchTargets(trgts);
		}

		/// <summary>
		/// Return true if this block has a single branch target at the end.
		/// </summary>
		public bool HasSingleBranchTarget
		{ 
			get 
			{ 
				return (this.m_bytecodes[this.m_bytecodes.Count - 1].OperationCode == OpCode.Op.BranchU); 
			} 
		}

		/// <summary>
		/// If this block has a unique branch target, return the target.
		/// </summary>
		public uint SingleBranchTarget
		{ 
			get 
			{
				Contract.Requires(this.HasSingleBranchTarget);

				return ((IRBranchOp)this.m_bytecodes[this.m_bytecodes.Count - 1]).BRUTarget; 
			} 
		}

		/// <summary>
		/// Add the given bytecode as the first op in the block.
		/// </summary>
		public void AddByteCodeAtStartOfBlock(IRByteCode s)
		{
			Contract.Requires(s != null);

			this.m_bytecodes.Insert(0, s); 
		}

		/// <summary>
		/// Add the given bytecode at the end of the block.
		/// </summary>
		public void AddByteCodeAtEndOfBlock(IRByteCode s)
		{
			Contract.Requires(s != null);

			this.m_bytecodes.Add(s); 
		}

		/// <summary>
		/// Add the given bytecode to the end of the block (but before the jump operation).
		/// </summary>
		public void AddByteCodeAtEndOfBlockButBeforeJump(IRByteCode s)
		{
			Contract.Requires(s != null);

			this.m_bytecodes.Insert(this.m_bytecodes.Count - 1, s); 
		}

		/// <summary>
		/// Add the given bytecodes to the start of the block.
		/// </summary>
		public void AddByteCodeListAtStartOfBlock(List<IRByteCode> sl)
		{
			Contract.Requires(sl != null);

			this.m_bytecodes.InsertRange(0, sl); 
		}

		public List<IRByteCode> Operations
		{ get { return this.m_bytecodes; } }
		
		public long BlockID 
		{ get { return this.m_blockID; } }

		public int BlockEntryUseModLoc
		{ get { return this.m_blockentryumloc; } }

		public override int MinStmt()
		{
			Contract.Assert(this.m_blockentryumloc != -1);

			return this.m_blockentryumloc;
		}

		public override int MaxStmt()
		{
			Contract.Assert(this.m_blockentryumloc != -1);

			return this.m_bytecodes[this.m_bytecodes.Count - 1].UseModLoc; 
		}

		/// <summary>
		/// Returns the set of vars that are in scope at the entry of this block.
		/// </summary>
		public HashSet<IRVarName> VarsInScopeAtEntry
		{ get { return this.m_inScopeAtEntry; } }

		/// <summary>
		/// Returns the set of vars that are live at the entry of this block.
		/// </summary>
		public HashSet<IRVarName> VarsLiveAtEntry
		{ get { return this.m_liveAtEntry; } }

		/// <summary>
		/// Returns a map from vars that are in scope at the entry of this block to their types.
		/// </summary>
		public Dictionary<IRVarName, IRType> BlockVarsToTypeMap
		{ get { return this.m_localstotypemap; } }

		//////////////////////////////////////////////////////////////////////////////////////////////////
		//Numbering and T1T2 operations

		public override void NumberRegion(CtrGen cg)
		{
			this.m_blockentryumloc = cg.GenNextVal();

			for(int i = 0; i < this.m_bytecodes.Count; ++i)
			{
				IRByteCode bc = this.m_bytecodes[i];
				bc.SetUseModLoc(cg);
			}
		}

		/// <summary>
		/// Prop and update live variables in this block and return true 
		/// </summary>
		internal override void PropLiveVariables(Dictionary<long, HashSet<IRVarName>> liveatexit, HashSet<IRVarName> addrtakenvars)
		{
			HashSet<IRVarName> lvexit = new HashSet<IRVarName>();
			List<uint> btrgts = new List<uint>();
			this.GetAllBranchTargets(btrgts);
			foreach(uint bt in btrgts)
				lvexit.UnionWith(liveatexit[bt]);

			for(int i = this.m_bytecodes.Count - 1; i >= 0; --i)
			{
				IRByteCode bc = this.m_bytecodes[i];
				if(bc.OperationCode == OpCode.Op.LoadFromLocal)
				{
					IRLoadToEvalStackBasic irll = (IRLoadToEvalStackBasic)bc;
					if(!addrtakenvars.Contains(irll.LocalVar))
					{
						irll.IsLocalDeadAfterLoad = !lvexit.Contains(irll.LocalVar);
						lvexit.Add(irll.LocalVar);
					}
				}
				else if(bc.OperationCode == OpCode.Op.StoreToLocal)
				{
					IRStoreFromEvalStackBasic irsl = (IRStoreFromEvalStackBasic)bc;
					if(!addrtakenvars.Contains(irsl.LocalVar))
						lvexit.Remove(irsl.LocalVar);
				}
				else
				{
					;
				}
			}

			//lvexit now contains the vars live at entry of the block
			liveatexit[this.m_blockID].Clear();
			liveatexit[this.m_blockID].UnionWith(lvexit);

			//lvexit now contains the vars live at entry of the block
			this.m_liveAtEntry.Clear();
			this.m_liveAtEntry.UnionWith(lvexit);
			this.m_liveAtEntry.UnionWith(addrtakenvars);
		}

		internal override void ComputeEvalStackExpressions(Dictionary<long, SymbolicEvalStack> evsts, IRProgram p)
		{
			SymbolicEvalStack evst = evsts[this.m_blockID].CopyStack();

			for(int i = 0; i < this.m_bytecodes.Count; ++i)
			{
				IRByteCode bc = this.m_bytecodes[i];

				bc.SetSymEvalStack(evst.CopyStack());

				SymbolicEvalStackTerm st1, st2, rt;
				IRType tmptype;
				switch(bc.OperationCode)
				{
					case OpCode.Op.Add:
					case OpCode.Op.AddU:
						st1 = evst.PopTerm();
						st2 = evst.PopTerm();
						rt = SymbolicEvalStackTerm.DoAdd(st1, st2);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.And:
					case OpCode.Op.Div:
					case OpCode.Op.DivU:
					case OpCode.Op.Mult:
					case OpCode.Op.MultU:
					case OpCode.Op.Or:
					case OpCode.Op.Rem:
					case OpCode.Op.RemU:
					case OpCode.Op.Shl:
					case OpCode.Op.Shr:
					case OpCode.Op.ShrU:
					case OpCode.Op.Sub:
					case OpCode.Op.SubU:
					case OpCode.Op.Xor:
						st1 = evst.PopTerm();
						st2 = evst.PopTerm();
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.NumericStackValueType);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.Greater:
					case OpCode.Op.GreaterU:
					case OpCode.Op.GreaterEQ:
					case OpCode.Op.GreaterEQU:
					case OpCode.Op.Less:
					case OpCode.Op.LessU:
					case OpCode.Op.LessEQ:
					case OpCode.Op.LessEQU:
					case OpCode.Op.PrimEQ:
						st1 = evst.PopTerm();
						st2 = evst.PopTerm();
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.NumericStackValueType);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.Neg:
					case OpCode.Op.Not:
						st1 = evst.PopTerm();
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.NumericStackValueType);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.AllocCons:
						IRAllocationOp allocop = (IRAllocationOp)bc;
						if(allocop.IsArrayAlloc)
							evst.PopTerm();
						else
						{
							for(int j = 0; j < allocop.ConsIdentity.Sig.InvokeSig.Count; ++j)
								evst.PopTerm();
						}

						if(allocop.AllocType is IRClassType)
							rt = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.LookupPointerTypeFor((IRClassType)allocop.AllocType));
						else
							rt = SymbolicEvalStackTerm.MakeUnknownTerm(allocop.AllocType);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.Box:
						st1 = evst.PopTerm();
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.LookupPointerTypeFor(p.TypeTable.LookupBoxedTypeFor((IRValueType)((IREvalStackTypeOp)bc).TypeArg)));
						evst.PushTerm(rt);
						break;
					case OpCode.Op.BranchC:
						evst.PopTerm();
						break;
					case OpCode.Op.BranchU:
						break;
					case OpCode.Op.Call:
					case OpCode.Op.CallVirt:
						IRCallOp callop = (IRCallOp)bc;
						SymbolicEvalStackTerm optidx = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.Int64Type);
						for(int j = 0; j < callop.InvokeSig.InvokeSig.Count; ++j)
							optidx = evst.PopTerm();

						SymbolicEvalStackTerm rcvr = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.NullType);
						bool rcvrokfortraversespec = false;
						HashSet<IRInvokeIdentity> allinvtrgts = new HashSet<IRInvokeIdentity>();
						if(callop.HasSpecificTarget)
						{
							if(callop.InvokeSig.IsVirtualInvoke)
							{
								rcvr = evst.PopTerm();
								rcvrokfortraversespec = (rcvr.SType is IRPtrType);
							}
							allinvtrgts.Add(p.InvokeTable.LookupInvokeIdentity(callop.SpecificTargetClass, callop.InvokeSig));
						}
						else
						{
							rcvr = evst.PopTerm();
							rcvrokfortraversespec = (rcvr.SType is IRPtrType);

							IRType rcvrtype = rcvr.SType;
							Tuple<IRType, IRInvokeSig> nettypeandvsig = new Tuple<IRType, IRInvokeSig>(callop.BaseImplementationType, callop.InvokeSig);
							allinvtrgts.UnionWith(p.GetPossibleInvocationTargetsForVirtCallExp(rcvrtype, nettypeandvsig));
						}

						if(callop.ReturnType != p.TypeTable.VoidType)
							evst.PushTerm(SymbolicEvalStackTerm.MakeUnknownTerm(ConvertToStackType(callop.ReturnType, p)));
						break;
					case OpCode.Op.Cast:
						IREvalStackTypeOp castop = (IREvalStackTypeOp)bc;
						st1 = evst.PopTerm();
						rt = SymbolicEvalStackTerm.ConvertType(castop.TypeArg, st1);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.Conv:
						IREvalStackTypeOp convop = (IREvalStackTypeOp)bc;
						st1 = evst.PopTerm();
						rt = SymbolicEvalStackTerm.ConvertType(ConvertToStackType(convop.TypeArg, p), st1);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.DerefableEQ:
						evst.PopTerm();
						evst.PopTerm();
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.NumericStackValueType);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.Dup:
						st1 = evst.PopTerm();
						evst.PushTerm(st1);
						evst.PushTerm(st1);
						break;
					case OpCode.Op.IndexRefAddrByArrayIndex:
						evst.PopTerm();
						st2 = evst.PopTerm();
						tmptype = ((IRContainerType)((IRPtrType)st2.SType).PtrTargetType).ContainerContentsType;
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.LookupReferenceTypeFor(tmptype));
						evst.PushTerm(rt);
						break;
					case OpCode.Op.IndexRefAddrByField:
						IRPointerIndexOp ptrop = (IRPointerIndexOp)bc;
						st1 = evst.PopTerm();
						rt = SymbolicEvalStackTerm.DoIndexOfField(st1, ptrop.IndexField, p.TypeTable.LookupReferenceTypeFor(ptrop.IndexField.FieldType));
						evst.PushTerm(rt);
						break;
					case OpCode.Op.IndirectInitialize:
						evst.PopTerm();
						break;
					case OpCode.Op.ArrayLength:
						evst.PopTerm();
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(ConvertToStackType(p.TypeTable.Int32Type, p));
						evst.PushTerm(rt);
						break;
					case OpCode.Op.ArrayLoad:
						optidx = evst.PopTerm();
						rcvr = evst.PopTerm();
						tmptype = ((IRContainerType)((IRPtrType)rcvr.SType).PtrTargetType).ContainerContentsType;
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(ConvertToStackType(tmptype, p));
						evst.PushTerm(rt);
						break;
					case OpCode.Op.IndirectRead:
						st1 = evst.PopTerm();
						rt = SymbolicEvalStackTerm.DoFieldIndirectLoad(ConvertToStackType(((IRReferenceType)st1.SType).ReferenceTargetType, p), st1);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.LoadMemberField:
						st1 = evst.PopTerm();
						IRLoadToEvalStackIndirect irrlf = (IRLoadToEvalStackIndirect)bc;
						rt = SymbolicEvalStackTerm.DoMemberFieldLoad(ConvertToStackType(irrlf.LoadField.FieldType, p), irrlf.LoadField, st1);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.ArrayStore:
						evst.PopTerm();
						evst.PopTerm();
						evst.PopTerm();
						break;
					case OpCode.Op.IndirectWrite:
						evst.PopTerm();
						evst.PopTerm();
						break;
					case OpCode.Op.StoreMemberField:
						evst.PopTerm();
						evst.PopTerm();
						break;
					case OpCode.Op.IsInst:
						evst.PopTerm();
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(((IREvalStackTypeOp)bc).TypeArg);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.Constrain:
						st1 = evst.GetTermAtPos(((IREvalStackTypeOp)bc).ConstrainPos);
						rt = SymbolicEvalStackTerm.ConvertType(((IREvalStackTypeOp)bc).TypeArg, st1);
						evst.SetTermAtPos(((IREvalStackTypeOp)bc).ConstrainPos, rt);
						break;
					case OpCode.Op.LoadFromGlobal:
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(ConvertToStackType(((IRLoadToEvalStackBasic)bc).StaticField.FieldType, p));
						evst.PushTerm(rt);
						break;
					case OpCode.Op.LoadFromLocal:
						rt = SymbolicEvalStackTerm.PushLocalVar(ConvertToStackType(this.BlockVarsToTypeMap[((IRLoadToEvalStackBasic)bc).LocalVar], p), ((IRLoadToEvalStackBasic)bc).LocalVar);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.LoadFuncPtr:
					case OpCode.Op.LoadVFuncPtr:
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.FunctionPtrType);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.LoadGlobalAddr:
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.LookupReferenceTypeFor(((IRPointerIndexOp)bc).StaticField.FieldType));
						evst.PushTerm(rt);
						break;
					case OpCode.Op.LoadLocalAddr:
						rt = SymbolicEvalStackTerm.PushLocalVarAddr(p.TypeTable.LookupReferenceTypeFor(this.BlockVarsToTypeMap[((IRPointerIndexOp)bc).LocalName]), ((IRPointerIndexOp)bc).LocalName);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.LoadStructField:
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(ConvertToStackType(((IRLoadToEvalStackIndirect)bc).LoadField.FieldType, p));
						evst.PushTerm(rt);
						break;
					case OpCode.Op.NonNull:
						evst.PopTerm();
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.NumericStackValueType);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.Pop:
						evst.PopTerm();
						break;
					case OpCode.Op.PushFloat:
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.NumericStackValueType);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.PushInt:
						if(((IRLoadToEvalStackBasic)bc).Literal.IntVal == 1)
							rt = SymbolicEvalStackTerm.PushConstantOne(p.TypeTable.NumericStackValueType);
						else
							rt = SymbolicEvalStackTerm.PushOtherConstant(p.TypeTable.NumericStackValueType);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.PushNull:
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.NullType);
						evst.PushTerm(rt);
						break;
					case OpCode.Op.PushString:
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.LookupPointerTypeFor(p.TypeTable.StringObjectType));
						evst.PushTerm(rt);
						break;
					case OpCode.Op.Return:
						break;
					case OpCode.Op.StoreToGlobal:
					case OpCode.Op.StoreToLocal:
						st1 = evst.PopTerm();
						break;
					case OpCode.Op.Switch:
						evst.PopTerm();
						break;
					case OpCode.Op.Throw:
						evst.ClearEvalStack();
						break;
					case OpCode.Op.UnBox:
						IREvalStackTypeOp ubxop = (IREvalStackTypeOp)bc;
						evst.PopTerm();
						rt = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.LookupReferenceTypeFor(ubxop.TypeArg));
						evst.PushTerm(rt);
						break;
					default:
						DebugReport.ReportError("Missing Case in Copy Loop Analysis");
						break;
				}
			}

			evst.SimplifyEvalStack();

			List<uint> btrgts = new List<uint>();
			this.GetAllBranchTargets(btrgts);
			foreach(uint bt in btrgts)
				evsts[bt] = evst.CopyStack();
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode")]
		internal override void DoLoopCopyAnalysis(bool findnewactions, long headblockid, long uniquebackblockid, IRProgram p, 
			ref bool iterok, ref IRVarName iterator, HashSet<IRCallOp> itergets, ref IRCallOp enumadv,
			ref bool indexok, ref IRVarName indexvar, ref IRMemberField optfield, HashSet<IRByteCode> indexgets,
			ref IRVarName indexpos, ref IRStoreFromEvalStackBasic indexstore, Dictionary<long, IRBasicBlock> idtobbmap)
		{
			for(int i = 0; i < this.m_bytecodes.Count; ++i)
			{
				IRByteCode bc = this.m_bytecodes[i];

				SymbolicEvalStackTerm st1;
				switch(bc.OperationCode)
				{
					case OpCode.Op.Call:
					case OpCode.Op.CallVirt:
						IRCallOp callop = (IRCallOp)bc;
						
						SymbolicEvalStackTerm rcvr = SymbolicEvalStackTerm.MakeUnknownTerm(p.TypeTable.NullType);
						bool rcvrokfortraversespec = false;
						if(callop.InvokeSig.IsVirtualInvoke)
						{
							rcvr = bc.SymEvalStack.GetTermAtPos(callop.InvokeSig.InvokeSig.Count);
							rcvrokfortraversespec = (rcvr.SType is IRPtrType);
						}

						
						HashSet<IRInvokeIdentity> allinvtrgts = new HashSet<IRInvokeIdentity>();
						if(callop.HasSpecificTarget)
							allinvtrgts.Add(p.InvokeTable.LookupInvokeIdentity(callop.SpecificTargetClass, callop.InvokeSig));
						else
						{
							IRType rcvrtype = rcvr.SType;
							Tuple<IRType, IRInvokeSig> nettypeandvsig = new Tuple<IRType, IRInvokeSig>(callop.BaseImplementationType, callop.InvokeSig);
							allinvtrgts.UnionWith(p.GetPossibleInvocationTargetsForVirtCallExp(rcvrtype, nettypeandvsig));
						}

						bool isenumeratorrcvr = rcvrokfortraversespec && IsTypeInstanceOfIEnumerator((IRPtrType)rcvr.SType);
						bool isilistrcvr = rcvrokfortraversespec && IsTypeInstanceOfIList((IRPtrType)rcvr.SType);
						bool restypeisptrorkvp = (callop.ReturnType is IRPtrType) || (callop.ReturnType is IRKeyValuePairType);

						////
						//Do checks on iterator loads
						if(isenumeratorrcvr && restypeisptrorkvp && callop.InvokeSig.InvokeName.Equals("get_Current"))
						{
							if(iterok && (rcvr.SymTerm == SymbolicEvalStackTerm.TermTag.Var || rcvr.SymTerm == SymbolicEvalStackTerm.TermTag.AddressOfVar) && (iterator == null || iterator == rcvr.TermRoot))
							{
								iterator = rcvr.TermRoot;
								itergets.Add(callop);
							}
							else
							{
								iterok = false;
								iterator = null;
								itergets.Clear();
								enumadv = null;
							}
						}
						
						////
						//Do checks on iterator advance
						if(isenumeratorrcvr && callop.InvokeSig.InvokeName.Equals("MoveNext"))
						{
							if(iterok && headblockid == this.m_blockID && (rcvr.SymTerm == SymbolicEvalStackTerm.TermTag.Var || rcvr.SymTerm == SymbolicEvalStackTerm.TermTag.AddressOfVar) && (iterator == null || iterator == rcvr.TermRoot))
							{
								iterator = rcvr.TermRoot;
								enumadv = callop;
							}
							else
							{
								iterok = false;
								iterator = null;
								itergets.Clear();
								enumadv = null;
							}
						}
						
						////
						//Do checks on list load
						if(isilistrcvr && restypeisptrorkvp && callop.InvokeSig.InvokeName.Equals("get_Item"))
						{
							SymbolicEvalStackTerm optidxl = bc.SymEvalStack.GetTermAtPos(0);

							if(indexok && rcvr.SymTerm == SymbolicEvalStackTerm.TermTag.Var && optidxl.SymTerm == SymbolicEvalStackTerm.TermTag.Var && ((indexvar == null && indexpos == null) || (indexvar == rcvr.TermRoot && indexpos == optidxl.TermRoot)))
							{
								indexvar = rcvr.TermRoot;
								indexpos = optidxl.TermRoot;
								indexgets.Add(callop);
							}
							else if(indexok && rcvr.SymTerm == SymbolicEvalStackTerm.TermTag.VarAndLoad && optidxl.SymTerm == SymbolicEvalStackTerm.TermTag.Var && ((indexvar == null && optfield == null && indexpos == null) || (indexvar == rcvr.TermRoot && optfield == rcvr.MemberField && indexpos == optidxl.TermRoot)))
							{
								indexvar = rcvr.TermRoot;
								optfield = rcvr.MemberField;
								indexpos = optidxl.TermRoot;
								indexgets.Add(callop);
							}
							else
							{
								indexok = false;
								indexvar = null;
								optfield = null;
								indexpos = null;
								indexgets.Clear();
								indexstore = null;
							}
						}

						break;
					case OpCode.Op.ArrayLoad:
						SymbolicEvalStackTerm optidxa = bc.SymEvalStack.GetTermAtPos(0);
						rcvr = bc.SymEvalStack.GetTermAtPos(1);

						if(((IRContainerType)((IRPtrType)rcvr.SType).PtrTargetType).ContainerContentsType is IRPtrType)
						{
							if(indexok && rcvr.SymTerm == SymbolicEvalStackTerm.TermTag.Var && optidxa.SymTerm == SymbolicEvalStackTerm.TermTag.Var && ((indexvar == null && indexpos == null) || (indexvar == rcvr.TermRoot && indexpos == optidxa.TermRoot)))
							{
								indexvar = rcvr.TermRoot;
								indexpos = optidxa.TermRoot;
								indexgets.Add(bc);
							}
							else if(indexok && rcvr.SymTerm == SymbolicEvalStackTerm.TermTag.VarAndLoad && optidxa.SymTerm == SymbolicEvalStackTerm.TermTag.Var && ((indexvar == null && optfield == null && indexpos == null) || (indexvar == rcvr.TermRoot && optfield == rcvr.MemberField && indexpos == optidxa.TermRoot)))
							{
								indexvar = rcvr.TermRoot;
								optfield = rcvr.MemberField;
								indexpos = optidxa.TermRoot;
								indexgets.Add(bc);
							}
							else
							{
								indexok = false;
								indexvar = null;
								optfield = null;
								indexpos = null;
								indexgets.Clear();
								indexstore = null;
							}
						}

						break;
					case OpCode.Op.StoreToLocal:

						////
						//Do checks on array index
						st1 = bc.SymEvalStack.PeekTerm();
						if(st1.SymTerm == SymbolicEvalStackTerm.TermTag.IncrementOfVar && st1.TermRoot == indexpos && uniquebackblockid == this.m_blockID)
							indexstore = (IRStoreFromEvalStackBasic)bc;

						break;
					default:
						break;
				}
			}
		}

		/// <summary>
		/// A helper to check if a type implements the generic IEnumerator interface.
		/// </summary>
		private static bool IsTypeInstanceOfIEnumerator(IRPtrType rcvrty)
		{
			IRType prtt = rcvrty.PtrTargetType;

			bool dirok = prtt.TypeName.StartsWith("System.Collections.Generic.IEnumerator<");
			bool indirok = prtt.TypeDescription.AllInheritsExtends.Any((IRType tt) => tt.TypeName.StartsWith("System.Collections.Generic.IEnumerator<"));

			return dirok | indirok;
		}

		/// <summary>
		/// A helper to check if a type implements the generic IList interface.
		/// </summary>
		private static bool IsTypeInstanceOfIList(IRPtrType rcvrty)
		{
			IRType prtt = rcvrty.PtrTargetType;

			bool dirok = prtt.TypeName.StartsWith("System.Collections.Generic.IList<");
			bool indirok = prtt.TypeDescription.AllInheritsExtends.Any((IRType tt) => tt.TypeName.StartsWith("System.Collections.Generic.IList<"));

			return dirok | indirok;
		}

		private static IRType ConvertToStackType(IRType tt, IRProgram p)
		{
			if(tt is IREnumType)
				tt = ((IREnumType)tt).UnerlyingNumericType;

			if(tt is IRSignedIntegerType || tt is IRUnSignedIntegerType || tt is IRFloatingPointType)
				return p.TypeTable.NumericStackValueType;
			else
				return tt;
		}

		private static SymbolicEvalStackTerm PopEvalTerm(List<SymbolicEvalStackTerm> evsts)
		{
			SymbolicEvalStackTerm tt = evsts[evsts.Count - 1];
			evsts.RemoveAt(evsts.Count - 1);
			return tt;
		}

		private static void PushEvalTerm(List<SymbolicEvalStackTerm> evsts, SymbolicEvalStackTerm evt)
		{
			evsts.Add(evt);
		}
	}
}
